package ws

import (
	"encoding/json"
	"errors"
	"goadmin/api"
	"goadmin/dao"
	"goadmin/model"
	"goadmin/utils"
	"log"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"github.com/gorilla/websocket"
)

var msgChan = make(chan []byte)
var conns = make(map[uint]*websocket.Conn)
var upgrader = websocket.Upgrader{
	ReadBufferSize:  4096,
	WriteBufferSize: 4096,
	CheckOrigin:     func(r *http.Request) bool { return true }, // allow any origin
}

// websocket connect, connect success, no response
// @Tags ws
// @Summary Websocket connect
// @Accept json
// @Produce json
// @Param token query string true "Token"
// @Router /ws [get]
func Ws(c *gin.Context) {
	var query struct {
		Token string `form:"token" binding:"required"`
	}
	if err := c.ShouldBindQuery(&query); err != nil {
		api.Res(c, api.CodeInvalidUrl, "invalid token")
		return
	}

	userId, err := utils.ParseToken(query.Token)
	if err != nil {
		if errors.Is(err, jwt.ErrTokenExpired) {
			api.Res(c, api.CodeTokenExpired, "token expired")
			return
		} else {
			api.Res(c, api.CodeNoAuth, "token invalid")
			return
		}
	}

	_, err = dao.GetUserById(userId)
	if err != nil {
		api.Res(c, api.CodeNoAuth, "user not found")
		return
	}

	conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		api.Res(c, api.CodeFail, "websocket connect failed")
		return
	}
	go connProc(userId, conn)
	// api.Res(c, api.CodeSuccess, "success")
	// 升级成功后不返回任何信息
}

func connProc(userId uint, conn *websocket.Conn) {
	defer func() {
		conn.Close()
		delete(conns, userId)
		subChan <- Sub{userId: userId, online: false}
		log.Printf("%d ws closed\n", userId)
	}()

	if connectConn, ok := conns[userId]; ok {
		// close the previous connection
		sendMsg(&model.Msg{
			FromId: userId,
			ToId:   userId,
			Type:   model.MsgTypeSystem,
			Body: model.SystemMsg{
				EventCode: model.EventCodeDisconnect,
				Event:     "连接已断开",
			}})
		connectConn.Close()
	}

	conns[userId] = conn
	subChan <- Sub{userId: userId, online: true}
	conversionList, err := dao.GetConversationListByUserId(userId)
	if err != nil {
		conversionList = make([]model.Conversation, 0)
	}
	welcomeMsg := model.Msg{
		FromId: 0,
		ToId:   userId,
		Type:   model.MsgTypeConversationList,
		Body: model.ConversationListMsg{
			Count:            int64(len(conversionList)),
			ConversationList: conversionList,
		},
		CreatedAt: time.Now(),
	}
	jsonBody, _ := json.Marshal(welcomeMsg)
	conn.WriteMessage(websocket.TextMessage, jsonBody)

	for {
		_, msgJson, err := conn.ReadMessage()
		if err != nil {
			log.Println(err)
			delete(conns, userId)
			return
		}

		msgChan <- msgJson
	}
}

func init() {
	go commonProc(msgChan)
	go subProc()
}
